﻿using SuperX.Common;
using SuperX.SuperSocket;
using Microsoft.AspNetCore.Mvc;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Writers;
using Swashbuckle.AspNetCore.SwaggerGen;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;

namespace SuperX.DataCenterService.WebAPI
{
    public class SwaggerCoreDocFilter : IDocumentFilter
    {
        public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
        {
            var list = RpcProcessor.InitActions();
            foreach (var method in list.Values)
            {
                Type Type = method.TypeObject.GetType();
                var MethodAttribute = method.MethodInfo.GetCustomAttributes(typeof(WebApiAttribute), false).FirstOrDefault() as WebApiAttribute;
                //路由
                var RewriteTypeName = Type.Name.Replace("Controller", "");
                var Routepath = $"/api/{RewriteTypeName}/{method.MethodName}";
                if (!swaggerDoc.Paths.ContainsKey(Routepath))
                {
                    swaggerDoc.Paths.Add(Routepath, new OpenApiPathItem
                    {
                        Summary = MethodAttribute.Summary,
                        Operations = new Dictionary<OperationType, OpenApiOperation>() { { OperationType.Post, GenerateOperation(RewriteTypeName, method, MethodAttribute, context) } }
                    });
                }
            }
        }

        private OpenApiOperation GenerateOperation(string tagName, ActionInfo methodInvokeInfo, WebApiAttribute webApiAttribute, DocumentFilterContext context)
        {
            return new OpenApiOperation
            {
                Summary = webApiAttribute.Summary,
                Description = webApiAttribute.Summary,
                Tags = new List<OpenApiTag> { new OpenApiTag { Name = tagName } },
                RequestBody = GenerateRequestBody(methodInvokeInfo, context),
                Responses = GenerateResponses(methodInvokeInfo, context)
            };
        }

        private OpenApiResponses GenerateResponses(ActionInfo methodInvokeInfo, DocumentFilterContext context)
        {
            try
            {
                var _200Response = new OpenApiResponse
                {
                    Description = methodInvokeInfo.Returns,
                    Content = new Dictionary<string, OpenApiMediaType>()
                    {
                        {"application/json", new OpenApiMediaType {Schema = context.SchemaGenerator.GenerateSchema(methodInvokeInfo.MethodInfo.ReturnType, context.SchemaRepository) } }
                    },
                };
                var responses = new OpenApiResponses
                {
                    {"200", _200Response}
                };
                return responses;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                Console.WriteLine(methodInvokeInfo.MethodInfo.ReturnType.FullName);
                return null;
            }
        }

        private OpenApiRequestBody GenerateRequestBody(ActionInfo methodInvokeInfo, DocumentFilterContext context)
        {
            return new OpenApiRequestBody
            {
                Content = new Dictionary<string, OpenApiMediaType>()
                {
                    {"application/json", new OpenApiMediaType {Schema = context.SchemaGenerator.GenerateSchema(methodInvokeInfo.MethodInfo.ReturnType, context.SchemaRepository) } }
                },
                Required = false,
                Description = GenerateRequestBodyMdDesc(methodInvokeInfo, context)
            };
        }

        private string GenerateRequestBodyMdDesc(ActionInfo methodInvokeInfo, DocumentFilterContext context)
        {
            var sb = new StringBuilder();

            var parameterInfoArray = methodInvokeInfo.MethodInfo.GetParameters();
            for (int i = 0; i < parameterInfoArray.Length; i++)
            {
                string desc = null;
                if (methodInvokeInfo.Params?.Length >= (i + 1))
                {
                    desc = methodInvokeInfo.Params[i];
                }
                var parameterInfo = parameterInfoArray[i];
                var schema = context.SchemaGenerator.GenerateSchema(parameterInfo.ParameterType, context.SchemaRepository);
                sb.Append($"{i + 1}. {parameterInfo.Name}: {Serial(schema)} {desc}");
                sb.Append("\n");
            }

            return sb.ToString();
        }

        private string Serial(OpenApiSchema schema)
        {
            using (var textWriter = new StringWriter(CultureInfo.InvariantCulture))
            {
                var jsonWriter = new OpenApiJsonWriter(textWriter);
                schema.SerializeAsV3(jsonWriter);

                return textWriter.ToString();
            }
        }
    }
}